package middleware

import (
	"fmt"
	"gitee.com/stevenchin/gin_study/response"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
	"runtime/debug"
	"time"
)

// Global 1. 观察执行顺序
func Global() gin.HandlerFunc {
	return func(context *gin.Context) {
		now := time.Now()
		fmt.Println("全局中间件开始执行了 ---> ")
		// 设置变量到Context的key中，可以通过 Get() 获取到
		context.Set("request", "中间件")
		// Next()方法：等待其他函数执行完毕后往下执行
		// TIPS：如果不执行context.Next()方法，最后一行打印的since将为0
		context.Next()
		// 执行完函数后的一些事情
		status := context.Writer.Status()
		fmt.Println("中间件执行完毕 <---", status)
		fmt.Println("time：", time.Since(now))
	}
}

// GlobalErrorHandler 统一异常处理
// TIPS：只能控制到路由级别的，如果不在路由对应的调用函数中，将不会捕获到。
// 例如在声明  xorm/initxorm/orm1.go:12 对应的全局变量 Engine 时，调用了 CreateEngine() 函数，如果函数内部 panic 时，将不会被捕获到，从而使得项目启动失败
func GlobalErrorHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		// panic异常一旦被引发就会导致程序崩溃。Go语言为开发者提供了专用于“拦截”运行时panic的内建函数recover()
		defer func() {
			if r := recover(); r != nil {
				log.Printf("运行时运发 panic，原因为: %v。\n具体错误堆栈信息如下：\n", r)
				//打印错误堆栈信息
				debug.PrintStack()
				//封装通用json返回
				response.Error(response.Data{
					Ctx:     c,
					Code: http.StatusInternalServerError,
					Message: errorToString(r),
				})
				//终止后续接口调用，不加的话 recover 到异常后，还会继续执行接口里后续代码
				c.Abort()
			}
		}()
		//c.Next()：加载完 defer recover，继续后续接口调用
		c.Next()
	}
}

// recover 错误转 string
func errorToString(r interface{}) string {
	switch v := r.(type) {
	case error:
		return v.Error()
	default:
		return r.(string)
	}
}
